package com.warm.system.service;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.warm.auth.helper.LoginHelper;
import com.warm.cache.utils.CacheUtil;
import com.warm.captcha.config.properties.CaptchaProperties;
import com.warm.core.constant.CacheConstants;
import com.warm.core.constant.Constants;
import com.warm.core.constant.TenantConstants;
import com.warm.core.constant.UserConstants;
import com.warm.core.exception.ServiceException;
import com.warm.core.exception.user.*;
import com.warm.core.utils.DateUtils;
import com.warm.core.utils.MessageUtils;
import com.warm.core.utils.StringUtils;
import com.warm.core.utils.ip.IpUtils;
import com.warm.core.utils.sign.RsaUtils;
import com.warm.orm.domain.sys.SysTenant;
import com.warm.orm.domain.sys.SysUser;
import com.warm.system.api.LoginUser;
import com.warm.system.api.model.LoginBody;
import com.warm.system.enums.UserStatus;
import com.warm.tenant.enums.TenantStatus;
import com.warm.tenant.exception.TenantException;
import com.warm.tenant.helper.TenantHelper;
import com.warm.thread.manager.AsyncManager;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;

/**
 * 登录校验方法
 *
 * @author warm
 */
@Component
public class SysLoginService {
    protected final Logger log = LoggerFactory.getLogger(this.getClass());

    @Inject
    private ISysUserService userService;

    @Inject
    private TokenService tokenService;

    @Inject
    private SysPasswordService passwordService;

    @Inject
    private SysPermissionService permissionService;

    @Inject
    private ISysTenantService tenantService;

    @Inject
    private CaptchaProperties captchaProperties;

    @Inject
    private ISysConfigService configService;

    /**
     * 登录验证
     *
     * @param loginBody 登录用户信息
     * @return 结果
     */
    public String login(LoginBody loginBody) {
        // 验证码校验
        validateCaptcha(loginBody);
        // 校验租户
        checkTenant(loginBody.getTenantId());
        // 登录前置校验
        loginPreCheck(loginBody.getTenantId(), loginBody.getUsername(), loginBody.getPassword());
        // 用户验证
        try {
            LoginUser loginUser = loadUserByUsername(loginBody);
            AsyncManager.executeRecord(loginBody.getUsername()
                    , Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
            recordLoginInfo(loginUser.getUserId());
            StpUtil.login(loginUser.getUserId());
            // 生成token
            return tokenService.createToken(loginUser);
        } catch (Exception e) {
            log.error("登录失败!", e);
            AsyncManager.executeRecord(loginBody.getUsername(), Constants.LOGIN_FAIL, e.getMessage());
            throw new ServiceException(e.getMessage());
        }
    }

    public void logout() {
        try {
            LoginUser loginUser = LoginHelper.getN();
            if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
                // 超级管理员 登出清除动态租户
                TenantHelper.clearDynamic();
            }
            if (ObjectUtil.isNotNull(loginUser)) {
                String userName = loginUser.getUsername();
                CacheUtil.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + ":" + loginUser.getToken());
                // 记录用户退出日志
                AsyncManager.executeRecord(userName, Constants.LOGOUT, "退出成功");
            }
        } catch (Exception e) {
            log.error("登出失败！", e);
        } finally {
            StpUtil.logout();
        }
    }

    private LoginUser loadUserByUsername(LoginBody loginBody) {
        SysUser user;
        if (TenantHelper.isEnable()) {
            user = userService.selectTenantUserByUserName(loginBody.getUsername(), loginBody.getTenantId());
        } else  {
            user = userService.selectUserByUserName(loginBody.getUsername());
        }
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户：{} 不存在.", loginBody.getUsername());
            throw new ServiceException(MessageUtils.message("user.not.exists"));
        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
            log.info("登录用户：{} 已被删除.", loginBody.getUsername());
            throw new ServiceException(MessageUtils.message("user.password.delete"));
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
            log.info("登录用户：{} 已被停用.", loginBody.getUsername());
            throw new ServiceException(MessageUtils.message("user.blocked"));
        }

        passwordService.validate(loginBody.getPassword(), user);
        LoginUser loginUser = createLoginUser(user);
        if (TenantHelper.isEnable())           {
            loginUser.setTenantId(loginBody.getTenantId());
        }
        return loginUser;
    }

    private LoginUser createLoginUser(SysUser user) {
        return new LoginUser(user.getUserId(), user.getDeptId(), user
                , permissionService.getMenuPermission(user), permissionService.getRolePermission(user));
    }

    /**
     * 校验验证码
     *
     * @param loginBody 用户登录信息
     * @return 结果
     */
    private void validateCaptcha(LoginBody loginBody) {
        if (captchaProperties.getEnable()) {
            String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StrUtil.emptyToDefault(loginBody.getUuid(), "");
            String captcha = CacheUtil.getCacheObject(verifyKey, String.class);
            CacheUtil.deleteObject(verifyKey);
            if (captcha == null) {
                AsyncManager.executeRecord(loginBody.getUsername()
                        , Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
                throw new CaptchaExpireException();
            }
            if (!loginBody.getCode().equalsIgnoreCase(captcha)) {
                AsyncManager.executeRecord(loginBody.getUsername()
                        , Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
                throw new CaptchaException();
            }
        }
        try {
            loginBody.setPassword(RsaUtils.decryptByPrivateKey(loginBody.getPassword()));
        } catch (Exception e) {
            log.error("密码解密失败！");
            throw new UserLoginException();
        }
    }

    /**
     * 登录前置校验
     *
     * @param username 用户名
     * @param password 用户密码
     */
    private void loginPreCheck(String tenantId, String username, String password) {
        // 用户名或密码为空 错误
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            AsyncManager.executeRecord(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null"));
            throw new UserNotExistsException();
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
            AsyncManager.executeRecord(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"));
            throw new UserPasswordNotMatchException();
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
            AsyncManager.executeRecord(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"));
            throw new UserPasswordNotMatchException();
        }

        // IP黑名单校验
        String blackStr = configService.selectConfigNoLogin(tenantId,"sys.login.blackIPList");
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
        {
            AsyncManager.executeRecord(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked"));
            throw new BlackListException();
        }
    }

    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    private void recordLoginInfo(Long userId) {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(IpUtils.getIpAddr());
        sysUser.setLoginDate(DateUtils.getNowDate());
        userService.updateUserProfile(sysUser);
    }

    /**
     * 校验租户
     *
     * @param tenantId 租户ID
     */
    public void checkTenant(String tenantId) {
        if (!TenantHelper.isEnable()) {
            return;
        }
        if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
            return;
        }
        if (StringUtils.isBlank(tenantId)) {
            throw new TenantException("tenant.number.not.blank");
        }
        SysTenant tenant = tenantService.queryByTenantId(tenantId);
        if (ObjectUtil.isNull(tenant)) {
            log.info("登录租户：{} 不存在.", tenantId);
            throw new TenantException("tenant.not.exists");
        } else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) {
            log.info("登录租户：{} 已被停用.", tenantId);
            throw new TenantException("tenant.blocked");
        } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
                && new Date().after(tenant.getExpireTime())) {
            log.info("登录租户：{} 已超过有效期.", tenantId);
            throw new TenantException("tenant.expired");
        }
    }
}
